﻿using System;
using System.Reflection;

namespace MyTestRunner.Domain
{
    /// <summary>
    /// Represents a class that can run tests.
    /// </summary>
    public class TestRunner
    {
        public TestRunResult Run(MethodInfo testMethod)
        {
            return Run(testMethod, null);
        }

        public TestRunResult Run(MethodInfo testMethod, Type expectedException)
        {
            return Run(testMethod, expectedException, null, null);
        }

        public TestRunResult Run(MethodInfo testMethod, Type expectedException, MethodInfo setUpMethod, MethodInfo tearDownMethod)
        {
            object testInstance = Activator.CreateInstance(testMethod.DeclaringType);
            return Run(testMethod, setUpMethod, tearDownMethod, expectedException, testInstance);
        }

        /// <summary>
        /// Runs the given test method.
        /// </summary>
        public TestRunResult Run(MethodInfo testMethod, MethodInfo setUpMethod, MethodInfo tearDownMethod, Type expectedException, object testInstance)
        {
            TestRunResult result;

            try
            {
                if (setUpMethod != null)
                {
                    setUpMethod.Invoke(testInstance, null);
                }

                testMethod.Invoke(testInstance, null);
            }
            catch (Exception e)
            {
                if (expectedException != null)
                {
                    if (e.InnerException.GetType() == expectedException)
                    {
                        return new TestRunResult(testMethod);
                    }
                }

                return new TestRunResult(testMethod, e.InnerException);
            }
            finally
            {
                try
                {
                    if (tearDownMethod != null)
                    {
                        tearDownMethod.Invoke(testInstance, null);
                    }
                }
                catch (Exception e)
                {
                    if (expectedException != null)
                    {
                        if (e.InnerException.GetType() == expectedException)
                        {
                            result = new TestRunResult(testMethod);
                        }
                    }
                    
                    result = new TestRunResult(testMethod, e.InnerException);
                }
            }

            return new TestRunResult(testMethod);
        }
    }
}
